﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using jabber;
using jabber.client;
using jabber.connection;
using jabber.protocol.x;

namespace iriska
{
    class Program
    {
        static ManualResetEvent _loggedIn = new ManualResetEvent(false);
        static ManualResetEvent _done = new ManualResetEvent(false);
        private static List<AbstractPlugin> _pluginList;

        static void Main(string[] args)
        {
            Log.WriteLine("Iriska started...");
            try
            {
                var assemblyLocation = Path.GetFullPath(Assembly.GetExecutingAssembly().Location);
                var plugins = Properties.Settings.Default.Plugins;
                _pluginList = new List<AbstractPlugin>();
                foreach (var plugin in plugins)
                {
                    if (string.IsNullOrEmpty(plugin))
                    {
                        Log.WriteLine("Invalid plugin name");
                        continue;
                    }

                    try
                    {
                        Log.WriteLine("Loading plugin {0}", plugin);
                        var assembly = Assembly.LoadFrom(plugin);
                        var pluginObject = InstantiatePlugin(assembly);
                        _pluginList.Add(pluginObject);

                        Log.WriteLine("\tok.");
                    }
                    catch (Exception exception)
                    {
                        Log.WriteLine(exception.ToString());
                    }
                }

                Run(_pluginList);
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                Log.WriteLine("Done.");
            }
        }

        private static void Run(List<AbstractPlugin> pluginList)
        {
            var client = new JabberClient();
            var settings = Properties.Settings.Default;
            client.User = settings.AccName;
            client.Password = settings.AccPassword;
            client.Server = settings.Server;

            client.OnAuthenticate += client_OnAuthenticate;
            client.OnInvalidCertificate += client_OnInvalidCertificate;
            client.OnAuthError += client_OnAuthError;
            client.OnError += client_OnError;

            client.Connect();
            client.Login();

            _loggedIn.WaitOne();

            if (client.IsAuthenticated)
            {
                _done.WaitOne();
            }
        }

        static void client_OnError(object sender, Exception ex)
        {
            Log.WriteLine(ex.Message);
            Log.WriteLine(ex.StackTrace);
        }

        static void client_OnAuthError(object sender, System.Xml.XmlElement rp)
        {
            Log.WriteLine("Iriska couldn't authenticate");
            _loggedIn.Set();
        }

        static bool client_OnInvalidCertificate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
        {
            return true; // connect anyway
        }

        static void client_OnAuthenticate(object sender)
        {
            Log.WriteLine("Logged in");
            _loggedIn.Set();
            JabberClient client = sender as JabberClient;
            if (client == null)
            {
                return;
            }

            var settings = Properties.Settings.Default;
            JID jid = new JID(settings.Conference, 
                settings.ConferenceServer, settings.Nickname);
            ConferenceManager manager = new ConferenceManager();
            manager.Stream = client;

            Room room = manager.GetRoom(jid);
            room.Join();

            room.OnRoomMessage += room_OnRoomMessage;
        }

        static void room_OnRoomMessage(object sender, jabber.protocol.client.Message msg)
        {
            try
            {
                Room room = sender as Room;
                var delays = msg.GetElements<Delay>();
                if (delays.Count == 0)
                {
                    foreach (var plugin in _pluginList)
                    {
                        try
                        {
                            plugin.Process(new ConfMessage(msg.Body, msg.From.Resource), room);
                        }
                        catch (Exception exception)
                        {
                            Log.WriteLine(string.Format("{0} throws exception {1}", plugin.PluginName(), exception.Message));
                            Log.WriteLine(exception.StackTrace);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                Log.WriteLine(exception.ToString());
            } 
        }

        private static AbstractPlugin InstantiatePlugin(Assembly assembly)
        {
            var types = assembly.GetTypes();
            foreach (var type in types)
            {
                if (type.IsSubclassOf(typeof(AbstractPlugin)))
                {
                    var pluginInstance = Activator.CreateInstance(type) as AbstractPlugin;
                    if (pluginInstance == null)
                    {
                        throw new Exception(
                            string.Format("Could not load plugin from assembly {0}", assembly.FullName));
                    }

                    return pluginInstance;
                }
            }

            throw new Exception(
                string.Format("Could not load plugin from assembly {0}", assembly.FullName));
        }
    }
}
